package bu.met.cs664;

/**
 * Player that uses a minimax algorithm to choose a Tic Tac Toe square
 */
public class MinimaxPlayer extends Player {

	/**
	 * Constructor
	 * 
	 * @param	id	identifier for the player
	 */
	MinimaxPlayer(int id) {
		super(id);
	}
	
	/**
	 * Recursive method to perform the minimax scoring of a given game board.
	 * 
	 * @param	parentBoard	game board to score
	 * @param	atkPlayer	player to maximize score
	 * @param	defPlayer	player to minimize score
	 * @param	depth		number calls to this method in the current call stack
	 * 
	 * @return	the minimax score
	 */
	private static int scoreBoard(Board parentBoard, Player atkPlayer, Player defPlayer, int depth)
	{
		int score = 0;
		
		if (parentBoard.won(atkPlayer)) {
			// Base case to maximize this outcome
			score = 1;
		} else if (parentBoard.won(defPlayer)) {
			// Base case to minimize this outcome
			score = -1;
		} else {
			// Recursive steps if parent board is not a draw
			int[] selections = parentBoard.getAvailableSelections();
			
			for (int i = 0; i < selections.length; i++) {
				Board childBoard = new Board(parentBoard);
				
				// Try out subsequent selections by the player who goes next.
				childBoard.select((((depth % 2) == 1) ? atkPlayer : defPlayer), selections[i]);
				
				score += scoreBoard(childBoard, atkPlayer, defPlayer, (depth + 1));
			}
		}
		
		return score;
	}
	
	/**
	 * Determine the next move for this player.
	 * 
	 * @param	board	the current game board
	 * 
	 * @return	the zero-based square to select
	 */
	public int nextMove(Board board) {	
		int[] selections = board.getAvailableSelections();
		assert(selections != null);
		assert(selections.length > 0);
		
		// Start with some non-optimal values so that they're overridden.
		int bestScore = Integer.MIN_VALUE;
		int bestSelection = Board.MAX_SELECTIONS;
		
		// Create a dummy opponent to beat.
		Player opponent = new MinimaxPlayer((isPlayerX()) ? PLAYER_O : PLAYER_X);
		
		// Decide.
		for (int i = 0; i < selections.length; i++)
		{
			Board childBoard = new Board(board);
			int score;
			
			childBoard.select(this, selections[i]);
			
			if (childBoard.won(this)) {
				// A winning move was found, so don't search anymore.
				bestSelection = selections[i];
				break;
			} else if (childBoard.blockWin(this, opponent)) {
				// This may be as good as it gets, but keep searching for a win.
				score = Integer.MAX_VALUE;
			} else {
				// Call the recursive function for the minimax score.
				score = MinimaxPlayer.scoreBoard(childBoard, this, opponent, 0);
			}
			
			if (score > bestScore) {
				bestScore = score;
				bestSelection = selections[i];
			}
		}
		
		assert(bestSelection < Board.MAX_SELECTIONS);
	
		return bestSelection;
	}
}
